package com.example.demo.tlwbus;


import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 总线的socket实现
 */
@Service
@Slf4j
public class BusSocketServiceImpl implements IBusSocketService {
    /**
     * 字符集
     */
    public static final Charset charset = Charset.forName("UTF-8");
    /**
     * 字节缓冲
     */
    public static final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    /**
     * 客户端连接池
     */
    public static volatile Map<String, BusSocketDTO> bus_client = Collections.synchronizedMap(new HashMap());

//    @Autowired
//    private IBusNetworkStatusService busNetworkStatusService;
//    @Autowired
//    private BusNetworkStatusDTOMapper busmapper;

    @Override
    @Async
    public void openWithServer(List<BusNetworkStatus> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (BusNetworkStatus busNetworkStatus : list) {
            this.open(busNetworkStatus);
        }
    }

    @Override
    @Async
    public void updateSocket(ResponseEntity<BaseDTO> baseDTOResponseEntity) {
        if (baseDTOResponseEntity == null || baseDTOResponseEntity.getBody() == null) {
            return;
        }
        BusNetworkStatus busNetworkStatus = null;
//                busmapper.DTOToDomain((BusNetworkStatusDTO) baseDTOResponseEntity.getBody());
        this.open(busNetworkStatus);
    }

    @Override
    public Boolean removeByIds(List<String> ids) {
        List<BusNetworkStatus> busNetworkStatuses = null;
//                busNetworkStatusService.listByIds(ids);
        for (BusNetworkStatus busNetworkStatus : busNetworkStatuses) {
            String key = com.example.demo.tlwbus.StringUtils.generBusKey(busNetworkStatus);
            if (StringUtils.isEmpty(key) || !bus_client.containsKey(key)) {
                continue;
            }
            Selector selector = bus_client.get(key).getSelector();
            this.removeSelector(selector);
            bus_client.remove(key);

        }
        return true;
    }

    @Override
    public Boolean readMsg(BusSocketDTO busSocketDTO, SelectionKey selectionKey) {
        Boolean flag = false;
        try {
            //获取连接通道
            SocketChannel channel = (SocketChannel) selectionKey.channel();
            //将数据读取到缓冲器中
            //服务端关闭，需要清除连接池，停止监听，并设置总线故障
            BusNetworkStatus busNetworkStatus = busSocketDTO.getBusNetworkStatus();
            if (channel.read(byteBuffer) <= 0) {
                //设置总线
                if (Objects.equals(busNetworkStatus.getLineStatus(), Constants.YES)
                        && Objects.equals(busNetworkStatus.getNetworkStatus(), Constants.YES)
                ) {
                    busNetworkStatus.setBreakdownTime(LocalDateTime.now());
                }
                busNetworkStatus.setLineStatus(Constants.NO);
                busNetworkStatus.setNetworkStatus(Constants.NO);

                //清除线程池
                try {
                    selectionKey.cancel();
                    bus_client.get(busSocketDTO.getKey()).getSelector().close();
                } catch (IOException e) {
                    log.error(Constants.SELECTOR_ERROR, e.getMessage());
                }
                bus_client.remove(busSocketDTO.getKey());
                //停止监听
                flag = false;
            }
            //接收到消息，需要设置总线的更新时间，网络状态，线路状态，
            //如果是从故障恢复需要设置恢复时间
            else {
                //读取消息
                StringBuffer stringBuffer = new StringBuffer();
                //反转缓冲到头部
                byteBuffer.flip();
                stringBuffer.append(charset.decode(byteBuffer));
                //清除缓冲器
                byteBuffer.clear();
                log.info("收到服务器 {}:{}，消息:{}", busNetworkStatus.getIp(), busNetworkStatus.getPort(), stringBuffer.toString());
                //回复给服务器 其实不需要回复
                channel.write(charset.encode(stringBuffer.toString()));
                if (Objects.equals(busNetworkStatus.getLineStatus(), Constants.NO)
                        || Objects.equals(busNetworkStatus.getNetworkStatus(), Constants.NO)
                ) {
                    busNetworkStatus.setRecoveryTime(LocalDateTime.now());
                }
                busNetworkStatus.setLineStatus(Constants.YES);
                busNetworkStatus.setNetworkStatus(Constants.YES);
                busNetworkStatus.setGmtModified(LocalDateTime.now());
                flag = true;
            }
//            busNetworkStatusService.saveOrUpdate(busNetworkStatus);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }


    /**
     * 建立链接
     *
     * @param busNetworkStatus
     * @return
     */
    @Async
    public void open(BusNetworkStatus busNetworkStatus) {
        BusSocketDTO busSocketDTO = this.init(busNetworkStatus);
        this.listen(busSocketDTO);
    }


    public void listen(BusSocketDTO busSocketDTO) {
        if (busSocketDTO == null || busSocketDTO.getSelector() == null) {
            return;
        }
        boolean flag = true;
        Selector selector = busSocketDTO.getSelector();
        try {
            while (flag) {
                //当注册的事件到达时，方法返回；否则,该方法会一直阻塞
                selector.select();
                //通信令牌
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    //删除令牌，避免冲突
                    iterator.remove();
                    //可读 代表有数据流入
                    if (selectionKey.isReadable()) {
                        flag = this.readMsg(busSocketDTO, selectionKey);
                    }
                }
            }
        } catch (Exception e) {
            bus_client.remove(busSocketDTO.getKey());
            try {
                selector.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }

    }

    @Override
    public void removeSelector(Selector selector) {
        Iterator<SelectionKey> iterator = selector.keys().iterator();
        try {
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isValid()) {
                    SocketChannel channel = (SocketChannel) selectionKey.channel();
                    channel.close();
                    selectionKey.cancel();
                    selector.close();
                }
            }

        } catch (IOException e) {
            log.error(Constants.SELECTOR_ERROR, e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 初始连接，
     * 线程池中有数据的，再判断网络状态是否是正常的，如果不正常，需要重新建立链接。
     * 初始化时 连接成功需要修改网络状态为成功
     * 连接失败 网络，线路 都修改为失败，还要记录故障时间，如果本来就是故障的，不需要记录
     *
     * @param busNetworkStatus
     * @return
     */
    public BusSocketDTO init(BusNetworkStatus busNetworkStatus) {
        if (busNetworkStatus == null) {
            return null;
        }

        String ip = busNetworkStatus.getIp();
        Integer port = Integer.parseInt(busNetworkStatus.getPort());
        String key = com.example.demo.tlwbus.StringUtils.generBusKey(busNetworkStatus);
        //连接池有数据，并且网络状态是正常的就不管了
        if (bus_client.containsKey(key) && Objects.equals(busNetworkStatus.getNetworkStatus(), Constants.YES)) {
            return null;
        }
        BusSocketDTO busSocket = Optional.ofNullable(bus_client.get(key)).orElse(new BusSocketDTO());
        //删除旧的连接
        try {
            if (busSocket.getSelector() != null) {
                bus_client.remove(key);
                busSocket.getSelector().close();
            }
        } catch (IOException e) {
            log.error("删除旧连接失败：{}", e.getMessage());
        }
        //连接服务端
        SocketChannel socketChannel;
        Selector selector = null;

        try {

            socketChannel = SocketChannel.open(new InetSocketAddress(ip, port));
            socketChannel.configureBlocking(false);
            log.info("连接服务端成功:{},{}", ip, port);
            selector = Selector.open();
            //将通道注册到选择器
            socketChannel.register(selector, SelectionKey.OP_READ);
            busNetworkStatus.setNetworkStatus(Constants.YES);
        } catch (IOException e) {
            if (Objects.equals(busNetworkStatus.getNetworkStatus(), Constants.NO)
                    || Objects.equals(busNetworkStatus.getLineStatus(), Constants.NO)
            ) {
                busNetworkStatus.setBreakdownTime(LocalDateTime.now());
            }
            busNetworkStatus.setNetworkStatus(Constants.NO);
            busNetworkStatus.setLineStatus(Constants.NO);
            log.error("连接服务端失败，{}:{},{}", ip, port, e.getMessage());
        }
//        busNetworkStatusService.saveOrUpdate(busNetworkStatus);
        if (selector == null) {
            return null;
        }
        //加入连接池
        busSocket.setBusNetworkStatus(busNetworkStatus);
        busSocket.setSelector(selector);
        busSocket.setKey(key);
        bus_client.put(key, busSocket);
        return busSocket;
    }
}
